package com.hup.utils.commons.thread;

import com.hup.utils.commons.exception.AppCommonException;
import com.hup.utils.commons.start.DialogHelper;
import com.hup.utils.commons.swing.ActionEventUtil;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;

import javax.swing.*;
import java.awt.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 实现任务[启动暂停]的线程控制功能类
 * @author hugan
 * @date 2020/2/28
 */
@Log4j2
public class PausableTask {

    private static final String TAG_PAUSE = "暂停";
    private static final String TAG_RESUME = "恢复";

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    /**
     * 用于唤醒 {@link #waitUntilFinish()}
     */
    private final Condition finishCondition = lock.newCondition();

    /**
     * 严格的线程安全式的调用;
     * 调用方法 {@link #isCanceled()} {@link #sleepIfPause()};
     * true-都使用lock,保证绝对的线程安全;
     * false-查询taskState时,不使用lock,降低性能损耗,适用于有频繁检查状态的情况;
     */
    private boolean threadSafeStrict;
    /**
     * 任务状态:
     * 扭转过程: READY->RUNNING [可选->PAUSE->RUNNING] [可选->CANCELED] ->READY
     */
    private TaskState taskState = TaskState.READY;
    @Getter
    private TaskResult taskResult = TaskResult.NOT_VALID;

    private JLabel labState;
    /**
     * 三者都可能是null
     */
    private JButton btnStart, btnPause, btnCancel;
    private Window parent;

    public PausableTask() {
        this(true);
    }

    /**
     * @param threadSafeStrict {@link #threadSafeStrict}
     */
    public PausableTask(boolean threadSafeStrict) {
        this.threadSafeStrict = threadSafeStrict;
    }

    public void bindState(JLabel labState) {
        this.labState = labState;
        labState.setText(taskState.name());
    }

    /**
     * @param btnStart notNull
     * @param btnPause nullable
     * @param btnCancel nullable
     */
    public void bindButtons(JButton btnStart, JButton btnPause, JButton btnCancel) {
        this.btnStart = btnStart;
        this.btnPause = btnPause;
        this.btnCancel = btnCancel;
        btnStart.addActionListener(e -> {
            if (ActionEventUtil.isAborted(e)) return;
            doStart();
        });
        if (btnPause != null) {
            btnPause.addActionListener(e -> {
                if (TAG_PAUSE.equals(btnPause.getText())) {
                    doPause();
                } else {
                    doResume();
                }
            });
        }
        if (btnCancel != null) {
            String tag = btnCancel.getText();
            btnCancel.addActionListener(e
                    -> DialogHelper
                    .yesNo("任务将在下一节点" + tag + ",确认?")
                    .okAction(this::doCancel)
                    .show(parent));
        }
        updateComponentState();
    }

    /**
     * 目前唯一功能: 当parent是置顶时,取消任务的确认框也是置顶的
     */
    public void bindFrame(Window parent) {
        this.parent = parent;
    }

    private void updateComponentState() {
        if (labState != null) labState.setText(taskState.name());
        switch (taskState) {
            case READY:
                setEnabled(btnStart, true);
                setVisible(btnPause, false);
                setEnabled(btnPause, true);
                setText(btnPause, TAG_PAUSE);
                setVisible(btnCancel, false);
                setEnabled(btnCancel, true);
                break;
            case RUNNING:
                setEnabled(btnStart, false);
                setVisible(btnPause, true);
                setEnabled(btnPause, true);
                setText(btnPause, TAG_PAUSE);
                setVisible(btnCancel, true);
                setEnabled(btnCancel, true);
                break;
            case PAUSE:
                setText(btnPause, TAG_RESUME);
                break;
            case CANCELED:
                setEnabled(btnPause, false);
                setEnabled(btnCancel, false);
                break;
        }
    }

    private static void setEnabled(JButton btn, boolean b) {
        if (btn != null) btn.setEnabled(b);
    }

    private static void setVisible(JButton btn, boolean b) {
        if (btn != null) btn.setVisible(b);
    }

    private static void setText(JButton btn, String str) {
        if (btn != null) btn.setText(str);
    }

    /**
     * 任务开始前调用,用于重置任务状态
     */
    public void doStart() {
        lock.lock();
        taskState = TaskState.RUNNING;
        taskResult = TaskResult.NOT_VALID;
        lock.unlock();
        updateComponentState();
    }

    //控制任务状态.st
    public void doPause() {
        lock.lock();
        taskState = TaskState.PAUSE;
        lock.unlock();
        updateComponentState();
    }

    public void doResume() {
        lock.lock();
        taskState = TaskState.RUNNING;
        condition.signalAll();//唤醒waitFinish中等待的线程
        lock.unlock();
        updateComponentState();
    }

    public void doCancel() {
        if (taskState == TaskState.READY) return;
        lock.lock();
        taskState = TaskState.CANCELED;
        condition.signalAll();//唤醒waitFinish中等待的线程
        lock.unlock();
        updateComponentState();
    }

    /**
     * 任务结束时,必须调用此方法;(包括正常结束和取消任务后);<br>
     * {@link #taskResult} 如果任务完成前,被标记成[取消],则任务结果为[已取消],否则为[已完成]
     */
    public void taskFinished() {
        lock.lock();
        if (taskState == TaskState.CANCELED) {
            taskResult = TaskResult.CANCELED;
        } else {
            taskResult = TaskResult.FINISH;
        }
        taskState = TaskState.READY;
        condition.signalAll();//正常时,不应该有这类线程被阻塞
        finishCondition.signalAll();//唤醒 waitUntilFinish
        lock.unlock();
        updateComponentState();
    }

    /**
     * 任务结束时,必须调用此方法;(包括正常结束和取消任务后);<br>
     * {@link #taskResult} 任务结果始终是[已完成](忽略中途标记过[取消]);处理虽然被[取消],但到达节点时任务确实已经完成的情况
     */
    public void taskFinishedWithNotCancel() {
        lock.lock();
        taskState = TaskState.READY;
        taskResult = TaskResult.FINISH;
        lock.unlock();
        updateComponentState();
    }
    //控制任务状态.ed

    /**
     * @return true-任务运行中/暂停中/取消中;false-任务未开始/已结束/已取消
     */
    public boolean isRunning() {
        if (threadSafeStrict) {
            lock.lock();
            boolean res = taskState != TaskState.READY;
            lock.unlock();
            return res;
        } else {
            return taskState != TaskState.READY;
        }
    }

    public boolean isCanceled() {
        if (threadSafeStrict) {
            lock.lock();
            boolean res = taskState == TaskState.CANCELED;
            lock.unlock();
            return res;
        } else {
            return taskState == TaskState.CANCELED;
        }
    }

    public void sleepIfPause() {
        if (threadSafeStrict) {
            lock.lock();
            try {
                if (taskState == TaskState.PAUSE) {
                    log.debug("sleep.st, t={}", Thread.currentThread().getName());
                    condition.await();
                    log.debug("sleep.ed, t={}", Thread.currentThread().getName());
                }
            } catch (InterruptedException e) {
                log.error("InterruptedException", e);
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        } else {
            if (taskState == TaskState.PAUSE) {
                try {
                    lock.lock();
                    log.debug("sleep.st, t={}", Thread.currentThread().getName());
                    condition.await();
                    log.debug("sleep.ed, t={}", Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    log.error("InterruptedException", e);
                    Thread.currentThread().interrupt();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    /**
     * 任务中的sleep,在cancel时可以被唤醒
     */
    public void sleep(long needSleep) {
        try {
            lock.lock();
            log.debug("sleep.st, t={}", Thread.currentThread().getName());
            condition.await(needSleep, TimeUnit.MILLISECONDS);
            log.debug("sleep.ed, t={}", Thread.currentThread().getName());
        } catch (InterruptedException e) {
            log.error("InterruptedException", e);
            Thread.currentThread().interrupt();
            throw new AppCommonException(e.toString());
        } finally {
            lock.unlock();
        }
    }

    public void waitUntilFinish() {
        if (isRunning()) {
            try {
                lock.lock();
                log.debug("waitUntilFinish");
                finishCondition.await();
            } catch (InterruptedException e) {
                log.error("InterruptedException", e);
                Thread.currentThread().interrupt();
                throw new AppCommonException(e.toString());
            } finally {
                lock.unlock();
            }
        }
    }

    private enum TaskState {
        /**
         * 任务已结束或未开始
         */
        READY(),
        /**
         * 运行中
         */
        RUNNING(),
        /**
         * 暂停中
         */
        PAUSE(),
        /**
         * 已取消,取消中
         */
        CANCELED(),
    }

    /**
     * 任务执行完的结果
     */
    public enum TaskResult {
        /**
         * 任务未开始或执行中
         */
        NOT_VALID(),
        /**
         * 正常完成
         */
        FINISH(),
        /**
         * 未完成且被取消
         */
        CANCELED(),
    }

}
